package com.example.lrucache;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;

class StringResource {

    public static final String INTERNAL_STATE_ERROR = "INTERNAL_STATE_ERROR";
    public static final String NOT_SUPPORT_METHOD = "NOT_SUPPORT_0_METHOD";
    public static final String NOT_SUPPORT_METHOD_WITH_PARAM = "NOT_SUPPORT_0_WITH_PARAMETER_1";
    public static final String PARAM_IS_NULL = "PARAMETER_0_IS_NULL";
    public static final String PARAM_OUT_OF_BOUND = "PARAMETER_0_OUT_OF_BOUND";
    public static final String PARAM_SHOULD_LTE_PARAM = "PARAMETER_0_SHOULD_LESS_THAN_OR_EQUAL_PARAMETER_1";

    private static final ResourceBundle bundle = ResourceBundle.getBundle("strings");

    private final LoadingCache<Key, String> cache = Caffeine.newBuilder()
            .expireAfterAccess(10, TimeUnit.MINUTES)
            .maximumSize(100)
            .recordStats()
            .build(k -> MessageFormat.format(bundle.getString(k.name), k.args));

    public void dumpCache() {
        cache.cleanUp();
        System.out.println(MessageFormat.format("Cache size: {0}", cache.estimatedSize()));
        System.out.println(Arrays.toString(cache.asMap().keySet().toArray()));
        System.out.println(cache.stats());
    }

    public String getString(String name, Object... args) {
        Key k = new Key(name, args);
        return cache.get(k);
    }

    @SuppressWarnings("ClassCanBeRecord")
    private static class Key {
        private final String name;
        private final Object[] args;

        Key(String name, Object... args) {
            this.name = name;
            this.args = args;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Key other = (Key) o;
            return Objects.equals(name, other.name) && Arrays.equals(args, other.args);
        }

        @Override
        public int hashCode() {
            int result = Objects.hash(name);
            result = 31 * result + Arrays.hashCode(args);
            return result;
        }

        @Override
        public String toString() {
            return "Key{" +
                    "name='" + name + '\'' +
                    ", args=" + Arrays.toString(args) +
                    '}';
        }
    }
}
